#include "pwqueue.hpp"

using namespace pw;

class QueueMessage;
class QueueThread : public pw::pwthread
{
public:
	QueueThread() :
		q_(0)
	{ }

	// the caller still owns and is responsible for
	// destruction of the queue
	void setQueue(pw::Queue *q)
	{
		q_ = q;
	}

	bool post(pw::Queue::Priority pri, pw::Message *m)
	{
		return q_->post(pri, m);
	}

	virtual void dispatch(QueueMessage *m) = 0;

	void *execute()
	{
		while(true) {
			pw::Message *m = q_->pop_front();
			{
				switch (m->msgtype_) {
				case pw::Message::EXIT:
					delete m;
					return 0;
				case pw::Message::NORMAL:
					dispatch((QueueMessage *)m);
					break;
				}
			}
		}
	}
protected:
	pw::Queue *q_; 
};

// [[ex06fa]]
// DEBUG descr The queue handler of the function bar().
class QueueMessage : public Message
{
public:
	QueueMessage(int type, int rettype, void *context) :
		type_(type), rettype_(rettype), context_(context)
	{ }

	int getType() { return type_; }
	int getRetType() { return rettype_; }
	void *getContext() { return context_; }

protected:
	// message type
	int type_; 
	// message type to be used for the reply
	int rettype_; 
	// context of the requestor
	void *context_; 

private:
	QueueMessage(); // no default constructor
};

class BarQueueThread : public QueueThread
{
public:
	enum BarMessageTypes {
		MSG_REQUEST
	};
	// the request that will be sent to bar
	class BarRequestMessage : public QueueMessage
	{
	public:
		int arg_; // the same argument as before
		QueueThread *requestor_; // where to send the reply

		BarRequestMessage(int rettype, void *context, 
				QueueThread *requestor, int arg) :
			QueueMessage(MSG_REQUEST, rettype, context),
			arg_(arg), requestor_(requestor)
		{ }
	};
	// results returned from bar
	class BarReplyMessage : public QueueMessage
	{
	public:
		int result_;

		BarReplyMessage(int type, void *context, 
				int result) :
			QueueMessage(type, -1, context),
			result_(result)
		{ }
	};

	BarQueueThread() :
		counter_(0)
	{ }

protected:
	void dispatch(QueueMessage *m) 
	{
		switch (m->getType()) {
		case MSG_REQUEST:
			processRequest((BarRequestMessage *)m);
			break;
		default:
			delete m;
			break;
		}
	}
	void processRequest(BarRequestMessage *m) 
	{
		counter_ += m->arg_;
		m->requestor_->post(Queue::PRIORITY_HIGH, 
			new BarReplyMessage(m->getRetType(), 
				m->getContext(), counter_));
		delete m;
	}

	int counter_;
};

// the way to find the bar thread, will be
// initialized from main()
static BarQueueThread *bar;
// END

